/**
* @file kheap.c
* Manages memory allocation over heap for whole kernel.
*
* Copyrights 2011 Michal Saman, m.saman@designplus.cz.
* This source code is release under the Apache License 2.0.
* http://www.apache.org/licenses/LICENSE-2.0
*/

#include <env.h>
#include <mem/kheap.h>
#include <mem/allocator.h>
#include <mem/vmu.h>

/* Global pointer to main heap of kernel */
heap_t* _kheap = NULL;

/**
 * Inicialize kernel memory heap.
 */
void kheap_init() {
	_kheap = init_allocator(KHEAP_INIT_SIZE, KHEAP_VIRT_BASE);
}

/**
 * Allocate memory in kernel main heap.
 */
uint32_t kmalloc(size_t size) {
	if (_kheap)
		return (uint32_t)alloc(size, 0, _kheap);
	else
		return (uint32_t)NULL;
}

/**
 * Free previously allocated memory in kernel heap.
 */
void kfree(void *p) {
	if (_kheap)
		free(p, _kheap);
}

/**
 * Allocate space in kernel heap and returns pointer to allocated memory (virtual and physical).
 */
uint32_t kmalloc_p(size_t size, bool page_align, uint32_t* phys_addr) {
	void* addr = NULL;

	if (_kheap) {	// kernel heap ready use it
		addr = alloc(size, page_align, _kheap);
		if (addr) {
			pte *p = vmu_get_page((virtual_addr) addr);
			//kprintf("PTE value: 0x%x\n", p);
			*phys_addr = pte_get_frame(*p);
		}
	} else {			// kernel heap not inicialized now, so init it with PMU alloc
		size = size / PMU_BLOCK_SIZE;
		if ((size % PMU_BLOCK_SIZE) > 0)
			size++;
		addr = pmu_alloc_blocks(size);
		if (addr)
			*phys_addr = addr;
	}

	return addr;
}

/*
 * Helper routine, test allocation/deallocation in kernel heap.
 */
void kheap_test() {
	unsigned i,x = 0;
	void* addr = NULL;

	for (i=0; i < 10; i++) {

		// TEST #01
		uint8_t* t1 = (uint8_t*) kmalloc_p(1, 1, &addr);
		kprintf("Addr: 0x%x, 0x%x\n", addr, t1);
		uint8_t* t3 = (uint8_t*) kmalloc_p(14, 1, &addr);
		kprintf("Addr: 0x%x, 0x%x\n", addr, t3);
		uint8_t* t4 = (uint8_t*) kmalloc_p(1, 1, &addr);
		kprintf("Addr: 0x%x, 0x%x\n", addr, t4);
		uint8_t* t5 = (uint8_t*) kmalloc_p(15, 1, &addr);
		kprintf("Addr: 0x%x, 0x%x\n", addr, t5);
		uint8_t* t2 = (uint8_t*) kmalloc_p(1, 1, &addr);
		kprintf("Addr: 0x%x, 0x%x\n", addr, t2);
		uint8_t* t6 = (uint8_t*) kmalloc_p(3, 1, &addr);
		kprintf("Addr: 0x%x, 0x%x\n", addr, t6);

		kfree(t1);			// ne 12
		kfree(t4);			// ne 12
		kfree(t2);			// ne 12
		kfree(t5);			// ano t5 + t4 + t2 = (12),(36),(BIG)

		kfree(t3);			// ano (12) + t3 + (36) = (60),(BIG)

		kfree(t6);			// ano t6 + (60) + (BIG) = (BIG)


		// TEST #02
		uint8_t* test1 = (uint8_t*) kmalloc(110);
		uint8_t* test2 = (uint8_t*) kmalloc(1);
		uint8_t* test3 = (uint8_t*) kmalloc(11);
		uint8_t* test4 = (uint8_t*) kmalloc(1);
		*test1 = 0xFF;
		*test2 = 0xEE;
		*test3 = 0xDD;
		*test4 = 0xCC;
		//kprintf("Test variable 0 at 0x%x contains: 0x%x\n", test0, *test0);
		//kprintf("Test variable 1 at 0x%x contains: 0x%x\n", test1, *test1);
		//kprintf("Test variable 2 at 0x%x contains: 0x%x\n", test2, *test2);
		//kprintf("Test variable 3 at 0x%x contains: 0x%x\n", test3, *test3);


		uint32_t* test5 = (uint32_t*) kmalloc(4);
		*test5 = 4294967295u;
		//kprintf("Test variable 4 at 0x%x contains: 0x%x\n", test4, *test4);


		uint32_t* test6 = (uint32_t*) kmalloc(4);							// TODO, kdyz se tu da 2 tak zahadne spadne
		*test6 = 4294967295u;
		//kprintf("Test variable 5 at 0x%x contains: 0x%x\n", test5, *test5);

		kfree(test4);
		kfree(test5);

		kfree(test6);

		kfree(test3);

		kfree(test1);
		kfree(test2);

	}

	kprintf("kheap test %u iterations done.\n", i);
}

/*
 * Show debug information about kernel heap.
 */
void kheap_status() {
	kprintf("kheap - show information about usage of main kernel memory heap - version 1.0\n");
	allocator_info(_kheap);
}
